#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import os
import platform
import re
import signal
import uuid
from typing import List

from common.common import execute_cmd, execute_cmd_list
from common.const import CMDResult
from common.file_common import delete_file
from oracle import logger
from oracle.common.common import get_oracle_file_paths, write_cert_2_file, \
    get_grid_home, login_oracle_database, exec_sql_cmd, parse_html_result

from oracle.common.constants import OracleFilePathSql, PexpectResult
from oracle.common.device_manager.constants import ReturnCode, StorageType, HyperPairAccessState
from oracle.common.device_manager.device_info import LunInfo, DeviceInfo
from oracle.common.device_manager.device_manager import DeviceManager
from oracle.common.device_manager.storage_common import StorageCommon
from oracle.common.powershell_service import PowershellService
from oracle.common.user_env_common import oracle_home_path, get_asm_user_env_by_linux
from oracle.common.windows_common import execute_windows_cmd
from oracle.schemas.oracle_schemas import OracleDiskInfo


def get_disk_info_by_asm_tool_in_windows_os(login_params, need_ocr_disk):
    pid = login_params.get("pid", "")
    db_instance_name = login_params.get("instance_name", "")
    install_user_name = login_params.get("db_install_user", "")
    cmd = 'asmtool -list'
    ret, std_out = PowershellService.exec_pw_shell(cmd)
    logger.debug(f"get_disk_num_by_asm_tool_in_windows_os:{std_out}")
    disk_infos = []
    if not ret:
        return disk_infos
    ocr_asm_disk_paths = get_ocr_asm_disk_paths_windows(pid, db_instance_name, install_user_name)
    logger.info(f"get_ocr_asm_disk_paths_windows :{ocr_asm_disk_paths}")
    for line in std_out.split('\n'):
        # 没有ORCLDISK标识的磁盘类型，过滤
        if not line or 'ORCLDISK' not in line:
            continue
        if not need_ocr_disk and check_is_ocr_path(line, ocr_asm_disk_paths):
            logger.info(f"is_ocr_path :{line}")
            continue
        disk_num = parse_oracle_disk_info_in_windows_os(line)
        if disk_num:
            disk_path = "Disk" + disk_num
            asm_path = line.split(' ')[0]
            disk_info = {"disk_path": disk_path, "disk_num": disk_num, "asm_path": asm_path}
            disk_infos.append(disk_info)
    # asm场景存在为加入asm的盘，需要查出所有非asm的盘进行备份
    disk_infos.extend(get_disk_info_no_asm_in_windows_os(login_params))
    return disk_infos


def check_is_ocr_path(path, ocr_asm_disk_paths):
    for ocr_asm_disk_path in ocr_asm_disk_paths:
        ocr_asm_disk_path = ocr_asm_disk_path.replace('\\\\.\\', '')
        if path.__contains__(ocr_asm_disk_path):
            return True
    return False


def get_disk_info_no_asm_in_windows_os(login_params):
    pid = login_params.get("pid", "")
    db_instance_name = login_params.get("instance_name", "")
    install_user_name = login_params.get("db_install_user", "")
    files_set = get_oracle_all_file_paths(pid, db_instance_name, install_user_name)
    logger.debug(f"files_set:{files_set}")
    disks = set()
    for path in files_set:
        # C:\APP\ORACLE\xxx，取盘符
        if not path:
            return []
        if path[0] == '+':
            # 排除asm路径，比如+DATA/HWDB/DATAFILE/
            continue
        symbol = str(path).split(':')[0].strip()
        disks.add(symbol)
    disk_infos = []
    for disk in disks:
        # 执行命令获取盘符对应的盘号：Get-Partition -driveletter c | select disknumber
        # 返回体：disknumber
        #        ----------
        #          0
        cmd = f'Get-Partition -driveletter {disk} | select disknumber'
        ret, std_out = PowershellService.exec_pw_shell(cmd)
        if not ret:
            return []
        disk_num = parse_disk_num_of_partition_in_windows_os(std_out)
        disk_path = "Disk" + disk_num
        disk_info = {"disk_path": disk_path, "disk_num": disk_num}
        disk_infos.append(disk_info)
    return disk_infos


def parse_disk_num_of_partition_in_windows_os(content: str):
    """
    windows下解析盘符号信息
    @param content: 需要解析的内容
    '', 'disknumber', '----------', '         1', '', '', ''
    @return num 盘符号：0
    """
    lines = content.split('\n')
    logger.debug(f"lines:{lines}")
    if len(lines) < 4:
        raise Exception('parse disk num of partition failed')
    return lines[3].strip()


def get_asm_disk_info_in_linux_os(login_params, asm_install_username, need_ocr_disk, is_cluster):
    # 存放磁盘名称：如：/dev/sda1
    disk_infos = []
    files_set = get_asm_disk_paths_for_backup(login_params, asm_install_username, need_ocr_disk, is_cluster)
    for path in files_set:
        real_path = os.path.realpath(path)
        disk_info = {
            "disk_path": real_path,
            "asm_path": os.path.basename(path)
        }
        disk_infos.append(disk_info)
    # asm场景存在为加入asm的盘，需要查出所有非asm的盘进行备份
    disk_infos.extend(get_no_asm_disk_info_in_linux_os(login_params))
    return disk_infos


def get_oracle_all_file_paths(pid, db_instance_name, install_user_name):
    files_set = set()
    files_set.update(get_oracle_file_paths(pid, db_instance_name, install_user_name, OracleFilePathSql.P_FILE.name))
    files_set.update(
        get_oracle_file_paths(pid, db_instance_name, install_user_name, OracleFilePathSql.SP_FILE.name))
    files_set.update(get_oracle_file_paths(pid, db_instance_name, install_user_name, OracleFilePathSql.DATA.name))
    files_set.update(
        get_oracle_file_paths(pid, db_instance_name, install_user_name, OracleFilePathSql.CONTROL.name))
    files_set.update(get_oracle_file_paths(pid, db_instance_name, install_user_name, OracleFilePathSql.LOG.name))
    return files_set


def parse_oracle_disk_info_in_windows_os(content: str):
    # 格式：ORCLDISKDATA0     \Device\Harddisk1\Partition2   499M
    logger.info(f"content:{content}")
    items = [item for item in content.strip().split(' ') if item]
    if len(items) < 3:
        return ''
    # 对\Device\Harddisk1\Partition2进行分割，取Harddisk1中的1
    for data in items[1].split('\\'):
        if 'Harddisk' not in data:
            continue
        return data.strip()[-1]
    return ''


def get_no_asm_disk_info_in_linux_os(login_params):
    pid = login_params.get("pid", "")
    db_instance_name = login_params.get("instance_name", "")
    install_user_name = login_params.get("db_install_user", "")
    files_set = get_oracle_all_file_paths(pid, db_instance_name, install_user_name)
    disks = set()
    for path in files_set:
        if not path or path[0] == '+':
            # 排除asm路径，比如+DATA/HWDB/DATAFILE/
            continue
        cmd = f"df -h {path}"
        return_code, std_out, std_err = execute_cmd(cmd)
        if return_code != CMDResult.SUCCESS.value:
            logger.error(f'df -h in get oracle file disk')
            return []
        # 格式：/dev/sda1
        real_path = parse_lvm_info_in_linux_os(std_out)
        logger.info(f"content: {std_out}, real_path: {real_path}")
        disks.add(real_path)
    disk_infos = []
    for disk in disks:
        disk_uuid = get_disk_uuid(disk)
        mount_point = get_disk_mount_point(disk)
        disk_info = {
            "disk_path": disk,
            'disk_uuid': disk_uuid,
            'mount_point': mount_point
        }
        disk_infos.append(disk_info)
    return disk_infos


def get_disk_mount_point(disk_path):
    cmd = f"df -h {disk_path}"
    return_code, std_out, std_err = execute_cmd(cmd)
    logger.info(f"get_disk_mount_point:std_out  {std_out}")
    if return_code != CMDResult.SUCCESS.value:
        raise Exception('get_disk_mount_point error')
    line = std_out.split('\n')[1]
    device = line.split()
    if len(device) < 6:
        raise Exception('get_disk_mount_point failed')
    return device[5]


def get_disk_uuid(disk_path):
    cmd = f"blkid -s UUID {disk_path}"
    return_code, std_out, std_err = execute_cmd(cmd)
    logger.info(f"get_disk_uuid:std_out {std_out}")
    if return_code != CMDResult.SUCCESS.value:
        logger.info(f"get_disk_uuid error")
        return ''
    result = std_out.strip().split('UUID=')
    if len(result) < 2:
        logger.info(f"get_disk_uuid failed")
        return ''
    return result[1].strip('"')


def parse_lvm_info_in_linux_os(content):
    pattern = r"on\s+(\S*)\s+(\S*)\s+(\S*)\s+(\S*)\s+(\S*)\s+(\S*)\n"
    tmp = re.search(pattern, content)
    # 格式：/dev/sda1  1014M  179M  836M  18% /boot
    elements = tmp if not tmp else tmp.groups()
    return elements[0]


def get_all_disk_num_unique_id_infos_in_windows_os():
    disk_infos = []
    # 格式：1    62C97B1C765400002C95834EBEE1995E
    cmd = 'get-disk| select number,uniqueid'
    ret, std_out = PowershellService.exec_pw_shell(cmd)
    if not ret:
        return disk_infos
    for line in std_out.split('\n'):
        if not line:
            continue
        items = [item for item in line.strip().split(' ') if item]
        if len(items) < 2:
            continue
        disk_wwn_info = OracleDiskInfo()
        disk_wwn_info.num = items[0]
        # windows的wwn信息中字母全为大写，而在v6 linux设备中字母全为小写，需要将其转换为小写
        disk_wwn_info.wwn = items[1].lower()
        disk_infos.append(disk_wwn_info)
    return disk_infos


def get_all_disk_num_wwn_infos_in_windows_os():
    disk_infos = []
    # 格式：1    62C97B1C765400002C95834EBEE1995E
    cmd = 'get-disk| select number,uniqueid'
    ret, std_out = PowershellService.exec_pw_shell(cmd)
    if not ret:
        return disk_infos
    for line in std_out.split('\n'):
        if not line:
            continue
        items = [item for item in line.strip().split(' ') if item]
        logger.info(f'get_all_disk_num_wwn_infos_in_windows_os items, {items}')
        if len(items) < 2 or not items[0].isdigit():
            continue
        disk_wwn_info = OracleDiskInfo()
        disk_wwn_info.num = items[0]
        # windows的wwn信息中字母全为大写，而在v6 linux设备中字母全为小写，需要将其转换为小写
        disk_wwn_info.wwn = items[1].lower()
        disk_infos.append(disk_wwn_info)
    return disk_infos


def get_disk_path_by_disk_num_in_windows_os(disk_num):
    # 根据disk_num获取盘符
    cmd = f'Get-Partition -disknumber {disk_num} | select driveletter'
    logger.info(f'get_disk_path_by_disk_num_in_windows_os cmd, {cmd}')
    ret, std_out = PowershellService.exec_pw_shell(cmd)
    if not ret:
        logger.error(f'get_disk_path_by_disk_num_in_windows_os std_out, {std_out}')
        return ''
    disk_path = parse_disk_path_of_partition_in_windows_os(std_out)
    logger.info(f'parse_disk_path_of_partition_in_windows_os disk_path, {disk_path}')
    return disk_path


def parse_disk_path_of_partition_in_windows_os(content: str):
    """
    windows下解析盘符信息
    @param content: 需要解析的内容
    disknumber
    ----------
    0
    @return num 盘符：C
    """
    lines = content.split('\n')
    logger.info(f'parse_disk_path_of_partition_in_windows_os lines, {lines}')
    if len(lines) < 4:
        raise Exception('parse disk path of partition failed')
    if not lines[-4]:
        return ''
    return lines[-4].strip()


def get_all_lun_wwn_info_in_linux_os(storage: dict = None):
    """
    获取linux系统的wwn信息
    @param storage: 存储快照信息
    @return lun wwn信息
    """
    scsi_flag = not storage or storage.get('transport_protocol') == 'iscsi' or storage.get(
        'transport_protocol') == 'fc' or storage.get('transport_protocol') == 'fc-scsi'
    if scsi_flag:
        # transport_protocol 传输协议，iscsi，nof，fc(fc-scsi,fc-nvme)
        return get_all_lun_wwn_info_in_linux_os_by_iscsi()
    elif storage.get('transport_protocol') == 'nof' or storage.get('transport_protocol') == 'fc-nvme':
        return get_all_lun_wwn_info_in_linux_os_by_nof(storage)
    return []


def get_all_lun_wwn_info_in_linux_os_by_iscsi():
    infos = []
    parent_path = '/dev/disk/by-id/'
    paths = os.listdir(parent_path)
    for path in paths:
        if 'wwn-' not in path and 'ultrapath' not in path:
            continue
        absolute_path = parent_path + path
        real_path = os.path.realpath(absolute_path)
        disk_wwn_info = OracleDiskInfo()
        wwn = path.split('-')[1]
        disk_wwn_info.wwn = wwn[2:]
        disk_wwn_info.disk_path = real_path
        infos.append(disk_wwn_info)
    return infos


def get_all_lun_wwn_info_in_linux_os_by_nof(storage):
    infos = []
    parent_path = '/dev/disk/by-id/'
    paths = os.listdir(parent_path)
    unique_num = str(uuid.uuid4())
    cert_path, crl_path = write_cert_2_file(storage.get('certification'), storage.get('revocationList'), unique_num)
    device_info = DeviceInfo(storage.get('ipList'), int(storage.get('port')), storage.get('username'),
                             storage.get('password'), storage.get('enableCert'), cert_path, crl_path)
    device_manager = DeviceManager.create_device_inst_by_ip_list(device_info, StorageType.DORADO)
    for path in paths:
        # nof协议用户有可能使用原生盘符也有可能使用ultrapath文件生成的盘符
        if 'nvme-eui.' not in path and 'ultrapath' not in path:
            continue
        absolute_path = parent_path + path
        real_path = os.path.realpath(absolute_path)
        disk_wwn_info = OracleDiskInfo()
        if 'nvme-eui.' in path:
            # nvme-eui.71009f997d26972fcc895e400000015f
            nguid = path.split('.')[1]
        else:
            # ultrapatha-0x71009f997d26972fcc895e400000015f
            nguid = path.split('-')[1][2:]
        lun_info = get_lun_info_by_nguid(nguid, device_manager)
        if not lun_info:
            logger.error(f"lun is not on this device nguid:{nguid}")
            continue
        disk_wwn_info.wwn = lun_info.wwn
        disk_wwn_info.disk_path = real_path
        infos.append(disk_wwn_info)
    delete_file(cert_path)
    delete_file(crl_path)
    return infos


def get_lun_info_by_wwn(wwn, device_manager: StorageCommon):
    lun_info = LunInfo()
    try:
        error_code, error_desc = device_manager.query_lun_by_wwn(wwn, lun_info)
    except Exception as exception_str:
        logger.error(f'get_lun_info_by_wwn failed, {exception_str}')
        error_code = ReturnCode.FAILED
    if error_code != ReturnCode.SUCCESS:
        lun_info = None
    return lun_info


def get_lun_info_by_nguid(nguid, device_manager: StorageCommon):
    lun_info = LunInfo()
    try:
        error_code, error_desc = device_manager.query_lun_by_nguid(nguid, lun_info)
    except Exception as exception_str:
        logger.error(f'query_lun_by_nguid failed, {exception_str}')
        error_code = ReturnCode.FAILED
    if error_code != ReturnCode.SUCCESS:
        lun_info = None
    return lun_info


def set_lun_info_for_disk_info(disk_infos, lun_infos: List, snapshots: List):
    logger.info(f'set_lun_info_for_disk_info start')
    for disk_info in disk_infos:
        for lun_info in lun_infos:
            if disk_info.get('wwn') == lun_info.wwn:
                disk_info['disk_size'] = int(int(lun_info.capacity) / 2)
                get_snapshot_by_lun_id(lun_info.lun_id, disk_info, snapshots)
    logger.info(f'set_lun_info_for_disk_info end {disk_infos}')
    return disk_infos


def get_asm_path_in_windows_os(disk_infos):
    logger.info(f"get asm path in windows os")
    cmd = 'asmtool -list'
    ret, std_out = PowershellService.exec_pw_shell(cmd)
    for disk_info in disk_infos:
        for line in std_out.split('\n'):
            if not line or 'ORCLDISK' not in line:
                continue
            disk_num = parse_oracle_disk_info_in_windows_os(line)
            if disk_num == disk_info.get('disk_num'):
                asm_path = line.split(' ')[0]
                disk_info['asm_path'] = asm_path
                break


def get_snapshot_by_lun_id(lun_id, disk_info, snapshots: List):
    for snapshot in snapshots:
        if snapshot.parent_id == lun_id:
            disk_info['local_wwn'] = disk_info['wwn']
            disk_info['wwn'] = snapshot.wwn
            return


def check_is_support_new_location_restore(pid, db_instance_name, install_user_name, asm_install_username):
    logger.info(f"check_is_support_new_location_restore")
    if not asm_install_username:
        asm_install_username = 'grid'
    ocr_asm_disk_paths = get_ocr_asm_disk_paths(pid, db_instance_name, install_user_name, asm_install_username)
    votefile_asm_disk_paths = get_votefile_asm_disk_paths(pid, db_instance_name, install_user_name,
                                                          asm_install_username)
    datafile_asm_disk_paths = get_asm_disk_paths_by_file_type(pid, db_instance_name, install_user_name,
                                                              OracleFilePathSql.DATA.name)
    logger.info(f"ocr {ocr_asm_disk_paths}, votefile {votefile_asm_disk_paths} datafile {datafile_asm_disk_paths}")
    if ocr_asm_disk_paths & datafile_asm_disk_paths or votefile_asm_disk_paths & datafile_asm_disk_paths:
        return 'data', ''
    logfile_asm_disk_paths = get_asm_disk_paths_by_file_type(pid, db_instance_name, install_user_name,
                                                             OracleFilePathSql.LOG.name)
    logger.info(f"logfile {logfile_asm_disk_paths}")
    if ocr_asm_disk_paths & logfile_asm_disk_paths or votefile_asm_disk_paths & logfile_asm_disk_paths:
        return '', 'log'
    return '', ''


def check_is_support_new_location_restore_windows(pid, db_instance_name, install_user_name):
    logger.info(f"check_is_support_new_location_restore_windows")
    ocr_asm_disk_paths = get_ocr_asm_disk_paths_windows(pid, db_instance_name, install_user_name)
    datafile_asm_disk_paths = get_asm_disk_paths_by_file_type(pid, db_instance_name, install_user_name,
                                                              OracleFilePathSql.DATA.name)
    logger.info(f"ocr {ocr_asm_disk_paths}, datafile {datafile_asm_disk_paths}")
    if ocr_asm_disk_paths & datafile_asm_disk_paths:
        return 'data', ''
    logfile_asm_disk_paths = get_asm_disk_paths_by_file_type(pid, db_instance_name, install_user_name,
                                                             OracleFilePathSql.LOG.name)
    logger.info(f"logfile {logfile_asm_disk_paths}")
    if ocr_asm_disk_paths & logfile_asm_disk_paths:
        return '', 'log'
    return '', ''


def get_asm_disk_paths_by_file_type(pid, db_instance_name, install_user_name, file_type):
    file_paths = get_oracle_file_paths(pid, db_instance_name, install_user_name, file_type)
    file_disks = set()
    for file_path in file_paths:
        if file_path.startswith('+'):
            file_disks.add(file_path.split('/')[0][1:])
    logger.info(f"get_asm_disk_paths_by_file_type, file_disks {file_disks}")
    return asm_disk_paths(pid, db_instance_name, install_user_name, file_disks)


def get_votefile_asm_disk_paths(pid, db_instance_name, install_user_name, asm_install_username):
    grid_home = get_grid_home(pid, asm_install_username)
    if not grid_home:
        logger.error(f"grid_home is empty")
        return set()
    crsctl_path = os.path.join(grid_home, 'bin', 'crsctl')
    cmd_list = [f"{crsctl_path} query css votedisk"]
    logger.info(f"query css votedisk, cmd {cmd_list}")
    return_code, std_out, std_err = execute_cmd_list(cmd_list)
    if return_code != CMDResult.SUCCESS:
        logger.error(f"query css votedisk failed, std_out {std_out} std_err {std_err}")
        return set()
    result_lines = std_out.strip().split('\n')
    result_lines_len = len(result_lines)
    if result_lines_len < 3:
        logger.error(f"query css votedisk failed, std_out {std_out}")
        return set()
    votefile_disks = set()
    result_lines = result_lines[2:-1]
    logger.info(f"query css votedisk, result_lines {result_lines}")
    for result_line in result_lines:
        logger.info(f"query css votedisk, result_line {result_line}")
        votefile_disk_str = result_line.split()[4]
        votefile_disk = votefile_disk_str[1:-1]
        votefile_disks.add(votefile_disk)
    logger.info(f"query css votedisk, votefile_disks {votefile_disks}")
    return asm_disk_paths(pid, db_instance_name, install_user_name, votefile_disks)


def get_ocr_asm_disk_paths(pid, db_instance_name, install_user_name, asm_install_username):
    asm_user_env = get_asm_user_env_by_linux(asm_install_username)
    cmd_list = [f"su - {asm_install_username} -c '{asm_user_env}ocrcheck'"]
    logger.info(f"get ocr disk, cmd {cmd_list}")
    return_code, std_out, std_err = execute_cmd_list(cmd_list)
    if return_code != CMDResult.SUCCESS:
        logger.error(f"get ocr disk failed, std_out {std_out} std_err {std_err}")
        return set()
    result_lines = std_out.strip().split('\n')
    ocr_disks = set()
    for line in result_lines:
        if not line or 'Device/File Name' not in line:
            continue
        ocr_disk_str = line.split(':')[1].strip()
        if ocr_disk_str.startswith('+'):
            ocr_disks.add(ocr_disk_str[1:])
    logger.info(f"get ocr disk, ocr_disks {ocr_disks}")
    return asm_disk_paths(pid, db_instance_name, install_user_name, ocr_disks)


def asm_disk_paths(pid, db_instance_name, install_user_name, disks):
    asm_paths = set()
    for disk in disks:
        cmd = f"select path from v$asm_disk " \
              f"where group_number = (select group_number from v$asm_diskgroup where name = '{disk}');"
        asm_disks = get_asm_disk_paths(pid, db_instance_name, install_user_name, cmd)
        for asm_disk in asm_disks:
            asm_paths.add(asm_disk.get('PATH'))
    return asm_paths


def get_asm_disk_paths(pid, db_instance_name, install_user_name, cmd):
    logger.info(f"Start get asm disk paths, pid: {pid}, database instance name: {db_instance_name}, "
                f"install user name: {install_user_name}.")
    result, child = login_oracle_database(pid, db_instance_name, install_user_name)
    if not result:
        raise Exception(f'Login database failed.')
    try:
        exec_status, std_out = exec_sql_cmd(child, cmd, PexpectResult.FILE_PATH)
    except Exception as exception:
        logger.error(f'exec cmd failed. {str(exception)}')
        raise Exception(f'exec sql cmd failed.') from exception
    finally:
        if platform.system().lower() == "windows":
            child.kill(signal.SIGTERM)
        else:
            child.close()
    if isinstance(std_out, bytes):
        std_out = std_out.decode('utf-8')
    if exec_status:
        if PexpectResult.FILE_PATH[3] in std_out:
            logger.warn(f"Failed to find file path: {cmd}, pid: {pid}.")
            return []
        path_list = parse_html_result(std_out)
        path_set = []
        for path in path_list:
            if path:
                path_set.append(path)
        logger.info(f"path_set:{path_set}")
        return path_set
    logger.error(f"Failed to execute cmd: {cmd}, pid: {pid}.")
    return []


def get_asm_disk_paths_for_backup(login_params, asm_install_username, need_ocr_disk, is_cluster):
    pid = login_params.get("pid", "")
    db_instance_name = login_params.get("instance_name", "")
    install_user_name = login_params.get("db_install_user", "")
    if not asm_install_username:
        asm_install_username = 'grid'
    logger.info(f"Start get oracle asm disk paths, pid: {pid}, database instance name: {db_instance_name}, "
                f"install user name: {install_user_name} is_cluster {is_cluster}.")
    cmd = "select name, path, group_number from v$asm_disk_stat;"
    asm_disk_list = get_asm_disk_paths(pid, db_instance_name, install_user_name, cmd)
    ocr_asm_disk_paths = set()
    votefile_asm_disk_paths = set()
    if is_cluster:
        ocr_asm_disk_paths = get_ocr_asm_disk_paths(pid, db_instance_name, install_user_name, asm_install_username)
        votefile_asm_disk_paths = get_votefile_asm_disk_paths(pid, db_instance_name, install_user_name,
                                                              asm_install_username)
    path_set = set()
    for asm_disk in asm_disk_list:
        logger.info(f"get_asm_disk_paths_for_backup:{asm_disk}")
        if asm_disk:
            asm_disk_path = asm_disk.get('PATH')
            # 排除ocr盘和voting file所在的盘
            if not need_ocr_disk and (asm_disk_path in ocr_asm_disk_paths or asm_disk_path in votefile_asm_disk_paths):
                continue
            path_set.add(asm_disk_path)
    logger.info(f"path_set:{path_set}")
    return path_set


def check_storage_device_access(storage, password, pid):
    cert_path, crl_path = write_cert_2_file(storage.get('certification'), storage.get('revocationList'), pid)
    device_info = DeviceInfo(storage.get('ipList'), int(storage.get('port')), storage.get('username'),
                             password, storage.get('enableCert'), cert_path, crl_path, False)
    # 校验当前agent与v6的连通性
    device_manager = DeviceManager.create_device_inst_by_ip_list(device_info, StorageType.DORADO)
    delete_file(cert_path)
    delete_file(crl_path)
    if device_manager is not None and device_manager.init_success:
        return True, device_manager
    return False, device_manager


def check_lun_metro_pair_status(lun_infos, hyper_metro_pairs):
    for lun in lun_infos:
        lun_hyper_metro_pair = None
        for hyper_metro_pair in hyper_metro_pairs:
            if lun.name == hyper_metro_pair.local_name:
                lun_hyper_metro_pair = hyper_metro_pair
                logger.info(f"check_metro_pair_storage_status {hyper_metro_pair}")
                break
        if not lun_hyper_metro_pair or lun_hyper_metro_pair.local_access_state != HyperPairAccessState.READ_WRITE:
            logger.error(f"check_metro_pair_storage_status failed {lun.name}")
            return False
    logger.info(f"check_metro_pair_storage_status success")
    return True


def get_ocr_asm_disk_paths_windows(pid, db_instance_name, install_user_name):
    query_cmd = "cmd /c ocrcheck"
    ret, output = execute_windows_cmd(pid, query_cmd.split(), "")
    if not ret:
        logger.error(f"get_ocr_asm_disk_paths_windows failed, std_out {output}")
        return set()
    logger.info(f"get_ocr_asm_disk_paths_windows, output {output}")
    result_lines = output.strip().split('\n')
    ocr_disks = set()
    for line in result_lines:
        if not line or 'Device/File Name' not in line:
            continue
        ocr_disk_str = line.split(':')[1].strip()
        if ocr_disk_str.startswith('+'):
            ocr_disks.add(ocr_disk_str[1:])
    logger.info(f"get ocr disk, ocr_disks {ocr_disks}")
    return asm_disk_paths(pid, db_instance_name, install_user_name, ocr_disks)


def get_asm_group_name(disk_path, db_install_user):
    group_name = ''
    cmd = f"su - {db_install_user} -c '{oracle_home_path} kfed read {disk_path} | grep grpname'"
    logger.info("get_asm_group_name cmd = %s", cmd)
    ret, std_out, std_err = execute_cmd(cmd)
    if not ret:
        logger.error("Get disk group name FAILED! output = %s", std_err)
        return group_name
    logger.info("Get disk group name SUCCEED! output = %s", std_out)

    if std_out and len(std_out.split()) > 1:
        if std_out.split()[1] != ';':
            group_name = std_out.split()[1]
    else:
        return group_name

    return group_name
